﻿/********
* Copyright © 深圳市新元素医疗技术开发有限公司 . All rights reserved.
* 
*  Description:
*  Create By $username at $time
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/

#region namespace

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cherry.Agile.Configuration;
using System.Collections.Concurrent;
using Cherry.Agile.ServiceModel.Interceptor;

#endregion

namespace Cherry.Agile.ServiceModel.Config {
    public class ClientConfigHelper {
        public class Endpoint {
            /// <summary>
            /// 失败后重连时间单位秒
            /// </summary>
            public int Timeout { get; set; }

            /// <summary>
            /// 服务器地址
            /// </summary>
            public string Address { get; set; }

            /// <summary>
            /// 权重
            /// </summary>
            public int Weight { get; set; }
        }

        public class ClientConfigItem {
            public string BaseAddress { get; set; }

            string _address = string.Empty;
            public string Address {
                set { _address = value; }
            }

            public string ServerGroup { get; set; }

            public string Assembly { get; set; }

            public bool IsBidirectional { get; set; }

            public List<Endpoint> Endpoints { get; set; }

            public string GetAddress(Type type) {
                return GetAddress(type, BaseAddress);
            }

            public string GetAddress<T>() {
                if (!string.IsNullOrEmpty(_address) && string.IsNullOrEmpty(BaseAddress))
                    return _address;

                return GetAddress(typeof(T), BaseAddress);
            }

            private string GetAddress(Type type, string address) {
                return DefaultEndpointPolicy.EndpointPolicy.GetAddress(type, address);
                //if (string.IsNullOrWhiteSpace(address))
                //    return string.Empty;

                //return address + "/" + type.FullName;
            }
        }

        #region private variable

        #endregion

        #region properies

        #endregion

        #region methods

        #region public
        static ConcurrentDictionary<RuntimeTypeHandle, ClientConfigItem> _clientConfigItems =
                new ConcurrentDictionary<RuntimeTypeHandle, ClientConfigItem>();
        public static ClientConfigItem GetConfig<T>() where T : class {
            var t = typeof(T);
            return GetConfig(t);
        }

        public static ClientConfigItem GetConfig(Type contract) {
            var t = contract;
            ClientConfigItem item = null;
            if (_clientConfigItems.TryGetValue(t.TypeHandle, out item))
                return item;

            var assemblyName = t.Assembly.FullName.Split(',')[0].Trim();
            var typeName = t.FullName;
            var q = ConfigItems.FirstOrDefault(c => (c.Type == typeName && c.Assembly == assemblyName));// || );
            if (q != null) {
                item = new ClientConfigItem() {
                    Address = q.Address,
                    Assembly = q.Assembly,
                    IsBidirectional = q.IsBidirectional,
                    ServerGroup = q.ServerGroup
                };
            } else {
                q = ConfigItems.FirstOrDefault(c => c.Assembly == assemblyName && c.Type == string.Empty);
                if (q != null) {
                    item = new ClientConfigItem() {
                        BaseAddress = q.Address,
                        Assembly = q.Assembly,
                        IsBidirectional = q.IsBidirectional,
                        ServerGroup = q.ServerGroup
                    };
                } else {
                    var temp = GetDefaultConfig();
                    item = new ClientConfigItem() {
                        BaseAddress = temp.BaseAddress,
                        Assembly = temp.Assembly,
                        IsBidirectional = temp.IsBidirectional,
                        ServerGroup = temp.ServerGroup
                    };
                }
            }
            item.IsBidirectional = Unity.GetIsBidirectionalSet(t, item.IsBidirectional);

            _clientConfigItems.TryAdd(t.TypeHandle, item);

            return item;
        }

        public static string GetAddress<T>() where T : class {
            return GetAddress(typeof(T));
        }

        public static string GetAddress(Type contract) {
            var config = ClientConfigHelper.GetConfig(contract);
            return config.GetAddress(contract);
        }

        public static bool IsEndPointExist(string configName) {
            return ExistsClientSettingName(configName);
        }
        #endregion

        #region protected

        #endregion

        #region private


        static ClientConfigItem defaultConfig;
        private static ClientConfigItem GetDefaultConfig() {
            if (defaultConfig == null) {
                var item = ConfigItems.FirstOrDefault(c => c.Assembly == "*");
                if (item != null)
                    defaultConfig = new ClientConfigItem() { 
                        BaseAddress = item.Address,
                        IsBidirectional = item.IsBidirectional,
                        Assembly = item.Assembly,
                        ServerGroup = item.ServerGroup };
                else
                    defaultConfig = new ClientConfigItem();
            }
            return defaultConfig;
        }



        //static Lazy<List<string>> _lazyChannelEndpointNames = new Lazy<List<string>>(GetExistsClientSettingName);
        static bool ExistsClientSettingName(string name) {
            List<string> vals = new List<string>();
            var clients = System.Configuration.ConfigurationManager.GetSection("system.serviceModel/client") as System.ServiceModel.Configuration.ClientSection;
            //Cherry.Agile.Configuration.ConfigManager.GetSection<System.ServiceModel.Configuration.ClientSection>("system.serviceModel/client");
            if (clients == null)
                return false;
            //vals.AddRange(clients.Endpoints.Cast<System.ServiceModel.Configuration.ChannelEndpointElement>().Select(c => c.Name));
            //return vals;
            return clients.Endpoints.Cast<System.ServiceModel.Configuration.ChannelEndpointElement>().FirstOrDefault(c => c.Name == name) != null;
        }

        //static Lazy<List<ClientElement>> _lazyItems = new Lazy<List<ClientElement>>(LoadItems);
        static List<ClientElement> ConfigItems {
            get {
                var section = Cherry.Agile.Configuration.AgileConfiguration.Current.Clients;
                return section.Cast<ClientElement>().OrderByDescending(c => c.Type).ToList();
            }
        }
        #endregion

        #endregion


    }

}
